home *** CD-ROM | disk | FTP | other *** search
- ;AX.25 packet driver for RS232 port by Pawel Jalocha
- ;version of 4th January 1993
-
- ;The purpose of this driver is to provide interface
- ;between simple modem (a la BAYCOM) and higher level application
- ;software like KA9Q NOS.
-
- ;ax25.com emulates HDLC chip, buffers frames
- ;and serves software interface to higher applications
- ;by mean of a selected software interrupt.
-
- ;Driver's calling convention conforms to "FTP packet driver specification"
- ;but no other tests than with NOS have been done.
-
- ;Currently the driver provides only _one_ handle at a time.
-
- ;To make ax25.com from ax25.asm execute two commands:
- ; tasm ax25
- ; tlink /t ax25
-
- ;Free license for this software is herein granted for all radio _amateurs_
- ;Commercial usage in whole or part is prohibited.
-
- ;Questions may be addressed to:
- ; email: jalocha@chopin.ifj.edu.pl
- ; or jalocha@vxcern.cern.ch
- ; packet: SR9VRC@SP9ZDN.POL.EU (don't know whether it really works)
-
- ; To change ... => look for definition of ...
- ;
- ;o Maximum frame size the driver can receive => MaxFrameLen
- ;
- ;o Maximum amount of data the driver can transmit per one PTT push
- ; and thus maximum transmition frame length => TxBufferLen
- ;
- ;o Size of an auxiliary buffer storing periods between signal
- ; transitions before they are analyzed => RxBufferLen
- ;
- ; If you don't need large frames you may save some kilobytes
- ; of resident RAM by making MaxFrameLen and TxBufferLen smaller.
- ;
- ; For low speeds you may as well carefully decrease RxBufferLen
-
- ;You may want to change drvr_class to SLIP and run point-to-point direct IP
-
- ;==============================================================
-
- ax25_code segment word public
- assume cs:ax25_code, ds:ax25_code
-
- org 2ch
- phd_environ dw ?
-
- org 100h ;when .com file is executed DOS jumps here
- start: jmp install_driver
-
- even
- ;primary parameters with default values
- packet_int_no db 60h ;software interrupt
- com_irq db 4 ;COM port IRQ
- com_irq_prio db 0 ;if non-zero the IRQ should have highest priority.
- com_irq_share db 0 ;if non-zero the IRQ is shared with other driver(s).
- com_base dw 3f8h ;COM port base
- bit_rate dw 1200 ;speed in bits per second
- tx_head dw 240 ;transmition header length in bits
- tx_tail dw 24 ;transmition tail length in bits
- slot_time db 120 ;slot time for carrier sensing in bits
- persistance db 64 ;p-persistence
- carrier_sense db 2 ;carrier sensing
- ;0 - don't care - just transmit (full duplex)
- ;1 - sense DCD line
- ;2 - sense data transitions (BAYCOM-like)
- ;3 - deliver from data analysis (software DCD)
- drvr_class db 9 ;driver's class - default is 9 that is AX.25
- sound db 0 ;sound effects enabled if non-zero
- walk_step_div db 16 ;bit_time/walk_step ratio
- dcd_thres dw 50 ;DCD squelch threshold (0..100)
-
- ;secondary parameters computed from primary ones
- cl_bit_len dw 0 ;bit len in system clock (8253/4) ticks
- cl_bit_len_2 dw 0 ;half bit len in clock ticks
- cl_dcd_thres dw 0 ;DCD threshold in clock ticks
- walk_step dw 0 ;random walk step for DPLL
- bd_slot_time dw 0 ;slot time in baud generator ticks / 8
- bd_bit_len db 0 ;bit len in baud generator ticks / 8
- irq_mask db 0 ;IRQ mask for 8259
-
- driver_name db 'AX.25 driver for BAYCOM-style modem',0
-
- even
- old_packet_int dw 0,0 ;saves software interrupt vector
- receive_upcall dw 0,0 ;keeps application "upcall" routine address
-
- ;ANSI sequences to set char. rendition.
- ;if you don't like these or have problems with ANSI
- ;make null strings definitions like: BLINK equ ''
-
- NORM equ 27,'[0m'
- BLINK equ 27,'[5m'
- REVERS equ 27,'[7m'
- BOLD equ 27,'[1m'
-
- ;==============================================================
- ;Service routine for software interrupt to control the driver
-
- ; Packet Driver Error numbers
- NO_ERROR equ 0 ;no error at all.
- BAD_HANDLE equ 1 ;invalid handle number
- NO_CLASS equ 2 ;no interfaces of specified class found
- NO_TYPE equ 3 ;no interfaces of specified type found
- NO_NUMBER equ 4 ;no interfaces of specified number found
- BAD_TYPE equ 5 ;bad packet type specified
- NO_MULTICAST equ 6 ;this interface does not support multicast
- CANT_TERMINATE equ 7 ;this packet driver cannot terminate
- BAD_MODE equ 8 ;an invalid receiver mode was specified
- NO_SPACE equ 9 ;operation failed because of insufficient space
- TYPE_INUSE equ 10 ;the type had previously been accessed, and not released.
- BAD_COMMAND equ 11 ;the command was out of range, or not implemented
- CANT_SEND equ 12 ;the packet couldn't be sent (usually hardware error)
- CANT_SET equ 13 ;hardware address couldn't be changed (more than 1 handle open)
- BAD_ADDRESS equ 14 ;hardware address has bad length or format
- CANT_RESET equ 15 ;Couldn't reset interface (more than 1 handle open).
- BAD_IOCB equ 16 ;an invalid iocb was specified
-
- regs_w struc ; stack offsets of incoming regs
- _ES dw ?
- _DS dw ?
- _BP dw ?
- _DI dw ?
- _SI dw ?
- _DX dw ?
- _CX dw ?
- _BX dw ?
- _AX dw ?
- _IP dw ?
- _CS dw ?
- _F dw ? ; flags, Carry flag is bit 0
- regs_w ends
-
- CY equ 0001h
- EI equ 0200h
-
- regs_b struc ; stack offsets of incoming regs
- dw ? ; es, ds, bp, di, si are 16 bits
- dw ?
- dw ?
- dw ?
- dw ?
- _DL db ?
- _DH db ?
- _CL db ?
- _CH db ?
- _BL db ?
- _BH db ?
- _AL db ?
- _AH db ?
- regs_b ends
-
- DRVR_ISR: ;service interrupt vector points here
- ;application layer calls enter this point
- ;via a software interrupt
- jmp exec_command ;entry point must be a jump
- db 'PKT DRVR',0 ;followed by this string
-
- exec_command: ;packet driver command executor
- sti ;don't lock interrupts
- push ax ;save registers on stack
- push bx
- push cx
- push dx
- push si
- push di
- push bp
- push ds
- push es
- mov bp,sp ;bp=sp so we can address pushed registers
- and _F[bp],not CY ;Clear carry on exit
- mov bx,cs ;make ds=cs
- mov ds,bx
- mov bl,ah ;execute command given by ah
- mov bh,0
- cmp bx,26
- jnc f_above_25
- add bx,bx
- call [functions+bx]
- DRVR_ISR_return:
- mov _DH[bp],dh ;pass dh-now to dh-on-exit
- sbb ax,ax
- and ax,CY
- or _F[bp],ax ;pass carry-now to carry-on-exit
- pop es
- pop ds
- pop bp
- pop di
- pop si
- pop dx
- pop cx
- pop bx
- pop ax
- iret
-
- f_above_25:
- call f_not_implemented
- jmp short DRVR_ISR_return
-
- even
- functions label word
- dw f_not_implemented ;0
- dw f_driver_info ;1
- dw f_access_type ;2
- dw f_release_type ;3
- dw f_send_pkt ;4
- dw f_terminate ;5
- dw f_get_address ;6
- dw f_reset_interface ;7
- dw f_stop ;8
- dw f_not_implemented ;9
- dw f_get_parameters ;10
- dw f_not_implemented ;11
- dw f_as_send_pkt ;12
- dw f_drop_pkt ;13
- dw f_not_implemented ;14
- dw f_not_implemented ;15
- dw f_not_implemented ;16
- dw f_not_implemented ;17
- dw f_not_implemented ;18
- dw f_not_implemented ;19
- dw f_set_rcv_mode ;20
- dw f_get_rcv_mode ;21
- dw f_set_multicast_list ;22
- dw f_get_multicast_list ;23
- dw f_get_statistics ;24
- dw f_set_address ;25
-
- f_driver_info:
- mov dh,drvr_class ;driver class
- mov _CH[bp],dh
- mov _AL[bp],1 ;basic flag
- mov _DX[bp],0 ;driver type
- mov _CL[bp],0 ;driver number
- mov _BX[bp],0 ;driver version
- mov _DS[bp],ds ;driver name pointer
- mov _SI[bp],offset driver_name
- mov dh,NO_ERROR
- clc
- ret
-
- f_access_type:
- mov bx,_BX[bp]
- cmp al,drvr_class ;our class ?
- jnz wrong_class
- cmp bx,0FFFFh ;generic type ?
- jz type_OK
- cmp bx,0 ;our type ?
- jnz wrong_type
- type_OK:
- cmp dl,0 ;generic num ?
- jnz wrong_num
- mov ax,receive_upcall ;check if handle busy
- or ax,receive_upcall+2
- jnz busy_handle
- mov receive_upcall,di ;store receiver upcall
- mov ax,es
- mov receive_upcall+2,ax
- mov _AX[bp],0 ;return handle=0
- clc
- mov dh,NO_ERROR
- ret
-
- wrong_class:
- stc
- mov dh,NO_CLASS
- ret
- wrong_type:
- stc
- mov dh,NO_TYPE
- ret
- wrong_num:
- stc
- mov dh,NO_NUMBER
- ret
- busy_handle:
- stc
- mov dh,TYPE_INUSE
- ret
-
- f_stop: jmp short clear_upcall
-
- f_release_type:
- cmp _BX[bp],0 ;handle=0 ?
- jnz wrong_handle
- mov ax,receive_upcall ;is receiver upcall defined ?
- or ax,receive_upcall+2
- jz wrong_handle ;jump if not
- clear_upcall:
- xor ax,ax ;receiver upcall := 0:0
- mov receive_upcall,ax ;this means "upcall address is not valid"
- mov receive_upcall+2,ax
- clc
- mov dh,NO_ERROR
- ret
-
- wrong_handle:
- stc
- mov dh,BAD_HANDLE
- ret
-
- f_send_pkt: ;_DS:si=data, cx=length
- mov es,_DS[bp] ;es:si=packet address, cx=packet length
-
- mov bx,cx ;save packet length
- mov ah,7Eh ;starting HDLC flag
- call AddTxByteDirect ;add to TxBuffer without bit stuffing
- jc PacketTooBig ;jump if TxBuffer full
- mov dx,0FFFFh ;initialize CRC
- AddNextByte:
- mov ah,es:[si] ;take next packet byte
- inc si ;increment pointer
- call CRCpass ;pass through CRC
- call AddTxByteStuffing ;add the byte to TxBuffer _with_ stuffing
- jc PacketTooBig ;jump if TxBuffer overloaded
- loop AddNextByte ;loop over packet's bytes
- not dx ;complete CRC computation by inverting all bits
- mov ax,dx ;append CRC
- xchg al,ah ;lower byte first
- call AddTxByteStuffing ;add to TxBuffer with stuffing
- jc PacketTooBig
- xchg al,ah ;higher byte now
- call AddTxByteStuffing ;add to TxBuffer
- jc PacketTooBig
- mov ah,7Eh ;ending HDLC flag
- call AddTxByteDirect ;add to TxBuffer _without_ bit stuffing
- jc PacketTooBig
- call TxFlush8bit ;ensure that TxBuffer data ends at byte boundary
- jz PacketTooBig
-
- call ValidateTxBlock ;make the packet we just put into buffer
- ;valid for transmition
-
- mov ax,bx ;increment bytes_out counter
- mov bx,offset bytes_out ;note that we still had the packet length in bx
- call inc_dword_bx_by_ax ;dword [bx]+=ax
-
- mov bx,offset packets_out ;increment packet_out counter
- call inc_dword_bx ;dword [bx]+=1
-
- ;Note that packets_out/bytes_out counts data sent by application
- ;that is _not_ the data transmitted from the buffer.
- ;Data sent by application will be transmitted on the air
- ;only after the driver goes into transmit state that is
- ;after it "pushed" PTT.
-
- clc
- mov dh,NO_ERROR
- ret
-
- PacketTooBig:
- call TxFlush8bit ;???
- call CancelTxBlock ;cancel the block we were writing
- ;into Tx buffer
- mov bx,offset errors_out ;increment errors_out counter
- call inc_dword_bx
-
- stc
- mov dh,CANT_SEND
- ret
-
- f_terminate:
- call deinstall_driver
- clc
- mov dh,NO_ERROR
- ret
-
- f_get_address: jmp short f_not_implemented
- f_reset_interface: jmp short f_not_implemented
- f_get_parameters: jmp short f_not_implemented
- f_as_send_pkt: jmp short f_not_implemented
- f_drop_pkt: jmp short f_not_implemented
- f_set_rcv_mode: jmp short f_not_implemented
- f_get_rcv_mode: jmp short f_not_implemented
- f_set_multicast_list: jmp short f_not_implemented
- f_get_multicast_list: jmp short f_not_implemented
-
- f_get_statistics:
- mov _DS[bp],ds
- mov _SI[bp],offset statistics_list
- clc
- mov dh,NO_ERROR
- ret
-
- f_set_address: jmp short f_not_implemented
-
- f_not_implemented: ;non-implemented functions jump here
- stc ;set carry to indicate an error
- mov dh,BAD_COMMAND ;error code = BAD_COMMAND
- ret
-
- even
- statistics_list label dword ;statistics variables
- packets_in dw 0,0 ;as in packet driver specification
- packets_out dw 0,0
- bytes_in dw 0,0
- bytes_out dw 0,0
- errors_in dw 0,0
- errors_out dw 0,0
- packets_dropped dw 0,0
-
- ;extended statistics (not in use yet)
- ;PTT_pushes dw 0,0
- ;HDLC_flags dw 0,0
- ;frame_aborts dw 0,0
- ;short_packets dw 0,0 ;counts valid but too short packets
-
- ;Simple routines to increment double words
-
- inc_dword_bx_by_ax: ;increments dword ds:[bx] by ax
- add word ptr [bx],ax
- inc bx
- inc bx
- adc word ptr [bx],0
- dec bx
- dec bx
- ret
-
- inc_dword_bx: ;increments dword ds:[bx] by 1
- add word ptr [bx],1
- inc bx
- inc bx
- adc word ptr [bx],0
- dec bx
- dec bx
- ret
-
- ;==============================================================
-
- even
- OldTimerISR dw 0,0 ;saves Timer interrupt service routine address
- TimerISRActive db 0
-
- ;This routine re-routes Timer interrupt so
- ;Timer_ISR is called at every timer tick
- Initialize_Timer:
- pushf ;save flags
- cli ;disable interrupts while we play with vectors
- ;is it really needed ?
-
- push ax ;save ax
- ;now save current timer int. vector
- push es ;save es and bx
- push bx
- mov al,8 ;save Timer interrupt vector
- mov ah,35h ;35h is "get vector" function
- int 21h ;call DOS service
- mov OldTimerISR,bx ;now vector in es:bx - we have to save it
- mov ax,es
- mov OldTimerISR+2,ax
- pop bx ;restore es and bx
- pop es
- ;set new vector
- push dx
- mov al,8 ;Timer vector is number 8
- mov ah,25h ;25h is "set vector" function
- mov dx,offset Timer_ISR ;ds:dx := Timer_ISR address
- int 21h
- pop dx
-
- pop ax ;restore ax
-
- popf ;restore flags (including interrupt flag)
- ret
-
-
- Restore_Timer: ;This routine restores Timer interrupt vector
- ;so Timer_ISR is not called any more
-
- pushf ;save flags
- push ax
- cli ;and disable interrupts
-
- ;restore interrupt vector
- push ds ;save ds
- push dx
- mov al,8 ;timer int. number
- mov ah,25h ;"set vector" function
- lds dx,dword ptr OldTimerISR ;ds:dx := vector address
- int 21h ;call DOS
- pop dx
- pop ds ;restore ds
-
- pop ax
- popf ;restore flags
- ret
-
- Timer_ISR:
- sti ;shall we enable interrupts already here ?
- pushf ;first call the old timer service routine
- call dword ptr cs:[OldTimerISR]
- push ax ;save all used registers
- push bx ;because we are in an interrupt
- push cx
- push dx
- push ds
- mov ax,cs ;make ds=cs
- mov ds,ax
- mov al,TimerISRActive ;check for possible recursive call
- and al,al
- jnz TimerISR_already_Active ;jump if it is so
- mov al,0FFh ;mark Timer ISR as active now
- mov TimerISRActive,al
- sti ;enable interrupts so Tx and Rx can run
- ;while we process data collected by Rx
- mov al,RxSoundActive ;if we activated receive beep before
- and al,al
- jz ReadNextRxPeriod
- in al,61h ;we terminate it now.
- and al,0FCh
- out 61h,al
- xor al,al
- mov RxSoundActive,al
-
- ReadNextRxPeriod:
- call ReadRxPeriod ;read next Rx period into ax
- jnc TimerISR_end ;jump if buffer empty
- call ProcessPeriod ;here we process the period
- jmp short ReadNextRxPeriod ;loop
- TimerISR_end:
- xor al,al
- mov TimerISRActive,al
- TimerISR_already_active:
- pop ds ;restore registers
- pop dx
- pop cx
- pop bx
- pop ax
- iret
-
- MaxFrameLen equ 2050 ;maximum frame length in bytes
- ;including CRC
- MinFrameLen equ 17 ;minimal length for AX.25 frame
- ;any shorter frame is discarded
-
- ;AX.25 frames have minimal length of 17 bytes but you may want to
- ;make this limit lower if you are going to run other protocol...
-
- even
- RxFrameData db MaxFrameLen dup(0) ;frame buffer
- RxFrameLen dw 0 ;actual frame length (counts bytes)
- RxBitCount db 0 ;count single data bits
- RxFrameValid db 0 ;non-zero if frame is valid
- SamplePhase dw 0 ;time to next sampling point
- SampleLevel dw 0 ;low=current data bit, high=prev. bit
- RxByteReg db 0 ;receiver shift register
- RxStuffing db 0
- RxShiftReg db 0
- RxSoundActive db 0
-
- ProcessPeriod: ;on input ax=period
- mov cx,SampleLevel ;bit 0,cl=level, bit 0,ch=previous level
- xor cl,1 ;flip level
- mov dx,SamplePhase ;dx=time to next sample
- MoveSampling:
- cmp ax,dx ;compare SamplePhase with period
- jc PeriodLower ;jump if Period lower
- sub ax,dx ;subtract SamplePhase from period
- xor ch,cl ;xor level with previous level
- call NewRxBit ;analyze bit in bit 0,ch
- mov ch,cl ;previous level = level
- mov dx,cl_bit_len ;load SamplePhase with bit length
- jmp short MoveSampling ;loop
- PeriodLower:
- sub dx,ax ;subtract period from SamplePhase
- mov SampleLevel,cx ;save SampleLevel
-
- ;now comes rather primitive DPLL
- mov ax,cl_bit_len_2 ;load half bit period
- sub ax,dx ;subtract SamplePhase
- ;now: dx=SamplePhase, ax=phase error
- cmp ax,walk_step
- jle check_neg
- add dx,walk_step
- jmp short save_SamplePhase
- check_neg:
- neg ax
- cmp ax,walk_step
- jle save_SamplePhase
- sub dx,walk_step
-
- ; add ax,8 ;offset error to compensate
- ; ;for "sar" arithmetic error
- ; mov cl,4 ;divide the error by 16
- ; sar ax,cl
- ; add dx,ax ;add correction to SamplePhase
-
- save_SamplePhase:
- mov SamplePhase,dx ;save SamplePhase
- ret
-
- NewRxBit: ;bit 0,ch = _inverted_ data bit to append to the frame
- ;ax,cx,dx must not be modified
- push ax
- push cx
- push dx
- mov al,RxShiftReg ;load shift reg.
- shl al,1 ;append data bit (which is still inverted)
- or al,ch
- mov RxShiftReg,al ;save shift reg.
- cmp al,81h ;check for HDLC flag (01111110 pattern)
- jz RxFoundFlag ;jump if so
- test al,7Fh ;check for invalid frame (7 1s in a row)
- jz RxFrameInvalid ;jump if so
-
- mov al,RxFrameValid ;is frame still valid ?
- and al,al
- jz NewRxBit_ret ;jump if so
- xor ch,1 ;invert bit to append (it was inverted at entry)
- mov al,RxByteReg ;load byte reg.
- mov ah,RxBitCount
- and ch,1 ;zero to append ?
- jz AppendZeroBit
- ror ch,1 ;carry=data bit (must be 1 here...)
- rcr al,1 ;append data bit to byte buffer
- inc ah ;increase bit counter
- inc RxStuffing ;increase stuffing flag
- jmp short CheckBitCount
- AppendZeroBit:
- xor cl,cl
- mov ch,RxStuffing ;check for stuffing
- cmp ch,5
- mov RxStuffing,cl ;clear stuffing flag
- jz NewRxBit_ret ;avoid adding zero bit
- shr al,1 ;append zero bit
- inc ah
- CheckBitCount:
- mov RxByteReg,al ;save byte reg.
- mov RxBitCount,ah ;save bit counter
- test ah,07h ;check for byte boundary
- jnz NewRxBit_ret
- mov bx,RxFrameLen ;load frame length
- cmp bx,MaxFrameLen ;check frame size
- jnc RxFrameInvalid ;jump if frame would exceed max. length
- mov [RxFrameData+bx],al
- inc bx
- mov RxFrameLen,bx ;save new frame length
- NewRxBit_ret:
- pop dx
- pop cx
- pop ax
- ret
- RxFrameInvalid:
- xor al,al
- mov RxFrameValid,al
- jmp short NewRxBit_ret
- RxFoundFlag:
- mov al,RxFrameValid ;frame valid ?
- and al,al
- jz PrepareNewFrame ;jump if not valid
- mov al,RxBitCount ;check bit count
- inc al
- test al,07h ;check if multiply of 8
- jnz PrepareNewFrame ;jump if not
- mov cx,RxFrameLen ;check frame length
- cmp cx,MinFrameLen
- jc PrepareNewFrame ;jump if length less then minimum
-
- mov dx,0FFFFh ;initialize CRC
- mov bx,offset RxFrameData ;pass frame bytes through CRC except last two
- sub cx,2 ;decrease length by 2
- PassNextRxbyte:
- mov ah,[bx] ;load next byte
- inc bx
- call CRCpass ;pass it through CRC
- loop PassNextRxByte
- not dx ;negate CRC
- cmp dl,[bx] ;check lower CRC byte
- jnz BadCRCFrame ;jump if bad
- inc bx
- cmp dh,[bx] ;check high CRC byte
- jnz BadCRCFrame ;jump if bad
-
- mov al,sound ;if sound effects activated
- and al,al
- jz UpCallAppl
- mov RxSoundActive,al ;make receive beep
- xor al,al
- out 42h,al
- mov al,2
- out 42h,al
- in al,61h
- or al,3
- out 61h,al
-
- UpCallAppl:
- ;Frame is OK !!! - do the upcall to the application layer.
- call DoUpCall
-
- PrepareNewFrame:
- xor ax,ax ;null frame lemgth
- mov RxFrameLen,ax
- mov RxBitCount,al ;null bit count
- mov RxStuffing,al ;initialize bit stuffing
- mov al,0FFh
- mov RxFrameValid,al ;mark frame as valid
- jmp short NewRxBit_ret
-
- BadCRCFrame: ;shall we really count bad CRC packets
- ;as "errors_in" ?
-
- mov bx,offset errors_in ;increment "errors_in"
- call inc_dword_bx
-
- jmp short PrepareNewFrame ;abort current frame and make ready for new one
-
- DoUpCall: ;input: RxFrameData contains a valid packet (CRC is OK)
- ; RxFrameLen contains its length
- push ds
- push es
- push si
- push di
-
- mov bx,offset packets_in ;increment input packet counter
- call inc_dword_bx
-
- mov cx,RxFrameLen ;load packet length
- sub cx,2 ;exclude CRC
-
- mov ax,cx ;increment input bytes counter by packet length
- mov bx,offset bytes_in
- call inc_dword_bx_by_ax
-
- mov ax,receive_upcall ;is there a valid upcall address ?
- or ax,receive_upcall+2
- jz drop_packet ;jump if there is not
-
- mov ax,0 ;flag=0 - first upcall
- mov bx,0 ;handle = 0
- mov di,0 ;set es:di = NULL
- mov es,di
- call dword ptr [receive_upcall] ;first upcall
- mov ax,es ;check if application returned
- or ax,di ;valid buffer pointer
- jz drop_packet ;jump if not
- ; jz DoUpCall_ret
-
- mov si,di ;make si=di before we alter di (for second upcall)
- mov bx,offset RxFrameData ;copy the packet to es:di
- mov cx,RxFrameLen ;packet length (exclude CRC)
- sub cx,2
- CopyLoop: mov al,[bx] ;loop over packet bytes
- inc bx ;would movsb do the job ?
- mov es:[di],al
- inc di
- loop CopyLoop
- mov cx,RxFrameLen ;again packet len for second call
- sub cx,2 ;and without CRC
- mov ax,es ;make ds=es (ds not same as cs now !)
- mov ds,ax
- mov bx,0 ;handle 0
- mov ax,1 ;flag=1 - second upcall
- call dword ptr cs:[receive_upcall] ;second upcall
- ;we have to use cs: addressing in above call because we modified ds
-
- DoUpCall_ret:
- pop di
- pop si
- pop es
- pop ds
- ret
-
- drop_packet:
- mov bx,offset packets_dropped ;increment dropped packet counter
- call inc_dword_bx
- jmp short DoUpCall_ret
-
- ;Note that packets_dropped counts packets refused by the application
- ;on the first upcall. I assume application refuses to take the packet
- ;by returning NULL pointer
-
- ;==============================================================
- even
- save_IER db 0
- save_LCR db 0
- save_MCR db 0
- save_DLL db 0
- save_DLM db 0
- save_irq_en db 0FFh ;save irq enabled in 8259
- save_ISR dw 0,0 ;saved interrupt vector
-
- initialize_COM:
- push ax
- push dx
- pushf ;save CPU interrupt flag
- cli ;disable interrupts
-
- push es
- push bx
- mov al,com_irq ;save COM interrupt vector
- add al,8
- mov ah,35h
- int 21h
- mov save_ISR,bx
- mov ax,es
- mov save_ISR+2,ax
- pop bx
- pop es
-
- mov al,com_irq ;set new vector
- add al,8
- mov ah,25h
- mov dx,offset COM_ISR
- int 21h
-
- mov ah,irq_mask
- not ah
- in al,21h ;read 8259 mask
- or al,ah ;extract com irq mask
- mov save_irq_en,al ;save it
- in al,21h ;enable com irq in 8259
- and al,ah ;by clearing the right bit.
- out 21h,al
-
- mov al,com_irq_prio ;IRQ priority requested ?
- and al,al
- jz save_com_reg ;jump if not
- mov al,com_irq
- sub al,1
- and al,07h
- or al,0C0h
- out 20h,al ;out 20h,(C0h + (irq-1)&7)
-
- save_com_reg: ;save COM registers
- mov dx,com_base ;dx=com_base
- inc dx ;dx=IER
- in al,dx ;save IER
- mov save_IER,al
- xor al,al ;disable all COM interrupts
- out dx,al
- add dx,2 ;dx=LCR
- in al,dx ;save LCR
- mov save_LCR,al
- inc dx ;dx=MCR
- in al,dx ;save MCR
- mov save_MCR,al
- dec dx ;dx=LCR
- mov al,81h
- out dx,al ;enable divisor read/write
- sub dx,3 ;dx=com_base=DLL
- in al,dx ;read DLL
- mov save_DLL,al ;save it
- inc dx ;dx=DLM
- in al,dx ;read DLM
- mov save_DLM,al ;save it
- dec dx ;dx=com_base=DLL
- mov ax,bd_slot_time ;set rate divisor
- ;to slot_time*bd_bit_len
- out dx,al
- inc dx
- xchg al,ah
- out dx,al
- add dx,2 ;dx=LCR
- mov al,01h ;set 6 data/1 stop/no parity format
- out dx,al
- inc dx ;dx=MCR
- mov al,09h ;set DTR high, RTS low.
- out dx,al ;and OUT2 high
- sub dx,3 ;dx=IER
- mov al,0Ah
- out dx,al ;enable TxEmpty and modem status interrupt
- add dx,4 ;dx=LSR
- in al,dx ;read LSR
- inc dx ;to clear possible line status int.
- in al,dx ;read MSR to clear possible modem status int
- sub dx,6 ;dx=com_base again
- in al,dx ;read Rx buffer
- in al,dx ;to clear any possible pending Rx int.
- mov al,000000b ;load Tx with 000000 char
- out dx,al
- out dx,al
-
- popf ;restore interrupt flag
- pop dx
- pop ax
- ret
-
- restore_COM:
- pushf
- push ax
- push dx
- cli
-
- push ds
- mov al,com_irq ;restore interrupt vector
- add al,8
- mov ah,25h
- lds dx,dword ptr save_ISR
- int 21h
- pop ds
-
- in al,21h ;restore int. enable in 8259
- or al,irq_mask
- and al,save_irq_en
- out 21h,al
-
- mov al,com_irq_prio ;was IRQ priority requested ?
- and al,al
- jz restore_com_reg ;jump if not
- mov al,0C7h
- out 20h,al ;out 20h,C7h
-
- restore_com_reg:
- mov dx,com_base
- in al,dx ;read Rx to clear a possible int.
- in al,dx
- inc dx ;dx=IER
- mov al,save_IER ;restore IER
- out dx,al
- add dx,2 ;dx=LCR
- mov al,save_LCR ;restore LCR
- out dx,al
- inc dx ;dx=MCR
- mov al,save_MCR ;restore MCR
- out dx,al
- inc dx ;dx=LSR
- in al,dx ;read LSR to clear possible int.
- inc dx ;dx=MSR
- in al,dx ;read MSR to clear possible int.
- sub dx,3 ;dx=LCR
- in al,dx ;enable rate divisor access
- or al,80h
- out dx,al
- sub dx,3 ;dx=com_base=DLL
- mov al,save_DLL ;restore rate divisor
- out dx,al
- inc dx ;dx=DLM
- mov al,save_DLM
- out dx,al
- add dx,2 ;dx=LCR
- mov al,save_LCR ;restore LCR again
- out dx,al
-
- pop dx
- pop ax
- popf
- ret
-
- even
- TxCountDown dw 0 ;Down-counter to measure Tx bits within a byte.
- TxState db 0 ;0 = idle
- ;1 = sending head
- ;2 = sending usefull data
- ;3 = sending tail
-
- COM_ISR:
- push ax ;save most often used registers on stack
- push bx
- push cx
- push dx
- push ds
-
- mov ax,cs
- mov ds,ax
- mov dx,com_base
- add dx,2 ;dx=IIR
-
- in al,dx ;load IIR
- test al,000000001b ;COM interrupt pending ?
- jnz No_COM_Service ;jump if not
-
- dec dx ;dx=IER
- xor al,al
- out dx,al ;disable all COM interrupts
- inc dx
- ;now check for possible interrupts sources
- call serv_modem_state ;dx=IIR and may not be changed
- call serv_tx_empty
-
- mov al,20h ;tell the interrupt controler
- out 20h,al ;that interrupt service is done.
-
- dec dx ;dx=IER
- mov al,0Ah
- out dx,al ;enable TxEmpty and Modem Status interrupts
- inc dx
-
- End_ISR:
- pop ds ;restore saved registers
- pop dx
- pop cx
- pop bx
- pop ax
- iret
-
- No_COM_service:
- mov al,20h
- out 20h,al
- jmp short End_ISR
-
- even
- prev_timer_count dw 0
-
- serv_modem_state: ;dx=IIR
- add dx,4 ;dx=MSR
- in al,dx ;read MSR
- sub dx,4 ;dx=IIR
- test al,00000001b ;did CTS change state ?
- ;one could check any other input here...
- jz serv_modem_state_ret ;jump if not
- xor al,al ;read system timer count
- out 43h,al
- in al,40h
- xchg al,ah
- in al,40h
- xchg al,ah ;Timer value in ax now
- mov bx,ax ;subtract previous count (Timer counts _down_ !)
- xchg ax,prev_timer_count
- sub ax,bx ;so now ax contains the period elapsed
- shr ax,1 ;shift right as lowest bit is always zero
- call StoreRxPeriod ;may not change dx,ax
- call UpdateDataStat
- serv_modem_state_ret:
- ret ;dx=IIR
-
- serv_tx_empty: ;dx=IIR
- add dx,3 ;dx=LSR
- in al,dx
- sub dx,3 ;dx=IIR
- test al,00100000b ;THRE ?
- jz serv_tx_empty_ret
- mov bx,2 ;for faster add/sub dx,2
- sub dx,bx ;dx=TxData
- xor al,al ;load Tx with 0000000 char
- out dx,al
- add dx,bx ;dx=IIR
- call [word ptr ServTx] ;bx=2, dx must _not_ be modified
- serv_tx_empty_ret:
- ret
-
- even
- ServTx dw ServTxIdle
-
- ServTxIdle: ;here a decision about pushing PTT should be taken
- call Randomize ;but first update RandomByte
- call TxBufferEmpty ;any data to transmit ?
- jz ClearStat ;jump if not
- call TxPTTDecision ;ask PTT decision circuit for permision
- jnc ClearStat ;jump if no permition
- ;decision positive - enter transmit mode
- add dx,2 ;dx=MCR
- in al,dx
- or al,2 ;set RTS high
- out dx,al ;thus activate PTT
- dec dx ;dx=LCR
- mov bl,bd_bit_len ;bx=bit length in baud divisor units
- xor bh,bh
- call SetTxBaudDiv_bx ;set baud generator to 1 bit len.
- dec dx ;dx=IIR
- mov ax,tx_head ;initialize count down
- mov TxCountDown,ax ;with Tx head len
- mov ServTx,offset ServTxHead ;Transmitter state is "head"
- ret
- ClearStat:
- call ClearDataStat
- ret
-
- even
- Tx8bit dw 0
- NextTxBit db 0
-
- ServTxHead:
- add dx,bx ;dx=MCR
- in al,dx ;flip DTR
- xor al,1
- out dx,al
- sub dx,bx ;dx=IIR
- dec TxCountDown
- jnz ServTxHead_ret
- mov ServTx,offset ServTxData ;enter data phase
- jmp short ReadNext8bit
- ServTxHead_ret:
- ret
-
- ServTxData:
- add dx,bx ;dx=MCR
- in al,dx ;flip DTR if next bit is 1
- xor al,NextTxBit
- out dx,al
- sub dx,bx ;dx=IIR
- mov ax,Tx8bit ;save bit counter and 8bit buffer
- dec ah ;decrement bit counter
- jz ReadNext8bit ;jump if zero
- ror al,1 ;rotate 8bit buffer
- mov Tx8Bit,ax ;save it
- and al,1 ;extract lowest bit
- mov NextTxBit,al ;save it
- ret
- ReadNext8bit:
- call ReadTx8bit ;Read next 8 bits
- jnc TxStartTail ;jump if buffer empty
- mov ah,8 ;bit counter = 8
- not al ;invert data bits
- mov Tx8Bit,ax ;save bit counter and 8bit buffer
- and al,1 ;leave lowest bit only
- mov NextTxBit,al
- ret
- TxStartTail:
- mov ax,tx_tail ;load count down with tx tail length
- mov TxCountDown,ax
- mov ServTx,offset ServTxTail
- ret
-
- ServTxTail: ;bx=2 at entry
- add dx,bx ;dx=MCR
- in al,dx ;flip DTR
- xor al,1
- out dx,al
- sub dx,bx ;dx=IIR
- dec TxCountDown
- jnz ServTxTail_ret
- add dx,bx ;dx=MCR
- in al,dx
- or al,1 ;set DTR high
- and al,0FDh ;and RTS low
- out dx,al
- dec dx ;dx=LCR
- mov bx,bd_slot_time ;set TxEmpty interrupt rate to slot time
- call SetTxBaudDiv_bx
- dec dx ;dx=IIR
- mov ServTx,offset ServTxIdle
- call ClearDataStat ;clear statistics for DCD
- ServTxTail_ret:
- ret
-
- SetTxBaudDiv_bx: ;input: bx=divisor value, dx=LCR
- mov al,81h ;enable divisor access
- out dx,al
- sub dx,3 ;dx=DLL
- mov ax,bx ;write in new baud rate
- out dx,al
- inc dx
- xchg al,ah
- out dx,al
- add dx,2 ;dx=LCR again
- mov al,01h ;disable divisor access
- out dx,al
- ret ;output: dx=LCR, ax,bx modified
-
- RandomWord dw 079BDh ;random number for p-persistance algorithm
-
- Randomize: ;make new random number
- mov cx,dx ;save dx
- xor al,al ;read system timer count
- out 43h,al
- in al,40h ;low byte
- xchg al,ah
- in al,40h ;high byte
- xchg al,ah ;timer value in ax now
- add ax,RandomWord ;add previous random word
- mov bx,65521 ;multiply by 65521
- mul bx ;dx:ax = ax * bx
- xor ax,dx
- mov RandomWord,ax ;save random word
- mov dx,cx ;restore dx
- ret ;ax,bx,cx modified
-
- TxPTTDecision: ;this routine decides whether to push PTT now
- ;input: dx=IIR
- mov al,carrier_sense
- and al,al ;Full duplex ?
- jz PushPTT ;jump if so
- call sense_carrier ;Is somebody else transmiting ?
- jc DontPushPTT ;jump if so
- ;now comes the p-persistance...
- mov ax,RandomWord ;ax=pseudo-random word
- xor ah,al ;ah=pseudo-random byte
- mov al,persistance ;al=persistance
- cmp ah,al ;carry when ah<persistance
- ret
-
- PushPTT:
- stc ;carry:=1 => push PTT
- ret
-
- DontPushPTT:
- clc ;carry:=0 => do not push PTT
- ret
-
- sense_carrier: ;input: al=carrier mode, dx=MCR
- ;output: carry=carrier state
-
- cmp al,1 ;sense DCD line ?
- jz sense_DCD
- cmp al,2 ;sense data transition
- jz sense_DataTrans
- cmp al,3 ;be more clever ?
- jz sense_data
- clc ;otherwise just say carrier=false
- ret
-
- sense_DCD:
- add dx,4 ;dx=MSR
- in al,dx ;read MSR
- sub dx,4 ;dx=IIR
- rcl al,1 ;carry=DCD state
- ret
-
- sense_DataTrans:
- xor ax,ax ;at least one data transition
- cmp ax,DataTransCount ;since previous time slot ?
- ret ;carry=1 if so
-
- sense_data:
- mov bx,DataTransCount
- cmp bx,2 ;more than 1 transitions counted ?
- jc few_trans ;jump if not
- mov cx,dx ;save dx
- mov ax,PeriodDevSum ;compute sum/count that is the average
- mov dx,PeriodDevSum+2
- div bx ;ax=periodDevSum div DataTransCount
- cmp ax,cl_dcd_thres ;is the average bigger than dcd_threshold ?
- mov dx,cx ;restore dx
- ret ;carry=0 (no carrier) if so
- few_trans:
- clc ;say carrier=false if there were only few transitions
- ret
-
- even
- DataTransCount dw 0 ;count input signal transitions
- PeriodDevSum dw 0,0 ;sums period deviations from round bit lenghts
- prev_period dw 0
-
- ClearDataStat: ;clear statistics for DCD
- xor ax,ax
- mov DataTransCount,ax
- mov PeriodDevSum,ax
- mov PeriodDevSum+2,ax
- ret
- UpdateDataStat: ;input: ax=period
- inc DataTransCount ;increment data transition counter
- mov bl,carrier_sense ;check carrier mode
- cmp bl,3 ;execute the rest only if carrier mode is 3
- jnz UpdateDataStat_ret
- push dx
- xor dx,dx
- xchg ax,prev_period ;prev_period=period;
- add ax,prev_period ;ax=period+prev_period
- add ax,cl_bit_len_2 ;period+=cl_bit_len/2
- mov bx,cl_bit_len
- div bx ;dx=(period+cl_bit_len_2) div cl_bit_len
- shr bx,1 ;bx=cl_bit_len/2
- sub dx,bx ;dx-=cl_bit_len/2
- jnc UpdateDevSum ;if result negative
- neg dx ;then negate it (we need absolute value)
- UpdateDevSum:
- xor ax,ax ;add dx to PeriodDevSum
- add PeriodDevSum,dx ;PeriodDevSum sums deviation of periods
- adc PeriodDevSum+2,ax ;from multiple bit lengths
- ;for DCD decision
- pop dx
- UpdateDataStat_ret:
- ret ;ax,bx modified
- ;==============================================================
-
- even
- TxBufferLen equ 4095 ;Tx buffer length in bytes - must be 2^n-1
- TxBuffer db TxBufferLen+1 dup(0) ;Tx buffer storage
- ;this buffer is filled by send_pkt routine
- ;and flushed by TxEmpty service routine
- ;when tx is in data phase
- ;buffer pointers
- TxReadPtr dw 0 ;points to next byte to read
- TxWritePtr dw 0 ;points to successor of the last valid byte
- TxBlockPtr dw 0 ;Temporary pointer while writing in data block
- ;this is to ensure that only complete blocks
- ;will be transmitted
-
- WriteTx8bit: ;input: al=byte to write
- push bx
- mov bx,TxBlockPtr ;load block end pointer
- mov [TxBuffer+bx],al ;store byte
- inc bx ;increase pointer
- and bx,TxBufferLen ;and flip it around
- cmp bx,TxReadPtr ;same as read ptr ?
- jz WriteTx_ret ;jump if so
- mov TxBlockPtr,bx
- WriteTx_ret:
- pop bx
- ret ;on exit: Z set = buffer full
- ;registers unchanged
-
- ValidateTxBlock:
- push bx
- mov bx,TxBlockPtr ;make write pointer same as block pointer
- mov TxWritePtr,bx
- pop bx
- ret ;registers unchanged
-
- CancelTxBlock:
- push bx
- mov bx,TxWritePtr ;make block pointer same as write pointer
- mov TxBlockPtr,bx
- pop bx
- ret ;registers unchanged
-
- ReadTx8bit:
- mov bx,TxReadPtr ;load read pointer
- cmp bx,TxWritePtr ;same as write pointer ?
- jz ReadTx_ret ;jump if so
- mov al,[TxBuffer+bx] ;read data into AL
- inc bx ;increment the read pointer
- and bx,TxBufferLen ;and flip it around
- mov TxReadPtr,bx ;save it
- stc ;set carry
- ReadTx_ret:
- ret ;if carry is 0 => buffer was empty
- ;bx is modified but this does not matter really
-
- even
- Tx8bitBuffer dw 8000h
-
- AddTxBit: ;input: bh=8bit reg., bl=1s counter
- inc bl ;increment 1s counter
- jc AddTxBit_1
- mov bl,0 ;clear counter when 0 bit
- AddTxBit_1:
- rcr bh,1 ;shift the bit into 8bit reg.
- jnc AddTxBit_ret
- mov al,bh ;write reg. into buffer
- call WriteTx8bit
- jz AddTxBit_err
- mov bh,80h
- AddTxBit_ret:
- clc ;clear carry => no problems
- ret
- AddTxBit_err:
- stc ;set carry => buffer overflow
- ret
-
- TxFlush8bit:
- push ax
- mov ax,Tx8bitBuffer
- mov al,ah
- and al,al
- jz TxFlush_ret
- clc
- TxFlush_l:
- rcr al,1
- jnc TxFlush_l
- call WriteTx8bit
- TxFlush_ret:
- mov ax,8000h
- mov Tx8bitBuffer,ax
- pop ax
- ret ;output: Z=1 means tx buffer overflow
-
- AddTxByteDirect: ;input: ah=byte to add
- push ax
- push bx
- push cx
- mov bx,Tx8bitBuffer
- mov cx,8
- AddNextBit:
- ror ah,1
- call AddTxBit
- jc AddTxByte_ret
- loop AddNextBit
- AddTxByte_ret:
- mov Tx8bitBuffer,bx
- pop cx
- pop bx
- pop ax
- ret ;output: carry=1 if buffer overflow
-
- AddTxByteStuffing: ;input: ah=byte to append _with_ bit stuffing
- push ax
- push bx
- push cx
- mov bx,Tx8bitBuffer
- mov cx,8
- AddNextBitS: ;loop over bits
- ror ah,1 ;copy next bit to carry flag
- call AddTxBit
- jc AddTxByteS_ret
- cmp bl,5
- jc AddTxByteS_l
- clc ;if more than 5 1s in a row
- call AddTxBit ;append an extra 0 bit
- jc AddTxByteS_ret ;jump if buffer overflow
- AddTxByteS_l:
- loop AddNextBitS
- clc
- AddTxByteS_ret:
- mov Tx8bitBuffer,bx
- pop cx
- pop bx
- pop ax
- ret ;output: carry=1 means buffer overflow
-
-
- TxBufferEmpty:
- mov bx,TxReadPtr
- cmp bx,TxWritePtr
- ret ;Z=1 means buffer is empty
- ;bx is modified
-
- even ;align buffer to word boudary
- RxBufferLen equ 1023 ;in words, must be 2^n-1
- RxBuffer dw RxBufferLen+1 dup(0) ;Rx buffer storing periods between CTS transition
- ;this buffer is filled by CTS transition
- ;interrupt routine and flushed
- ;by system timer service routine
- RxReadPtr dw 0 ;read pointer
- RxWritePtr dw 0 ;write pointer
-
- StoreRxPeriod: ;must not modify dx,cx
- mov bx,RxWritePtr ;load store pointer
- mov [RxBuffer+bx],ax ;store the period
- add bx,2 ;increment the pointer
- and bx,2*RxBufferLen ;turn it around if needed
- mov RxWritePtr,bx ;save it
- cmp bx,RxReadPtr ;same as ReadPtr ?
- jnz RxStore_ret ;jump if not
- mov bx,RxReadPtr
- add bx,2 ;increment the read pointer
- and bx,2*RxBufferLen ;and turn it around
- mov RxReadPtr,bx
- RxStore_ret:
- ret
- ;the above routine discards the oldest period when the buffer overflows
-
- ReadRxPeriod: ;modifies only ax
- push bx
- mov bx,RxReadPtr ;load read pointer
- cmp bx,RxWritePtr ;same as write pointer ?
- jz RxRead_ret ;jump if so
- mov ax,[RxBuffer+bx] ;read the period
- add bx,2 ;increase the pointer
- and bx,2*RxBufferLen ;turn it around
- mov RxReadPtr,bx ;save it
- stc ;set carry => data is in ax
- RxRead_ret:
- pop bx
- ret ;if carry is 0 => then buffer was empty
- ;otherwise ax = period
-
- ;==============================================================
- ;CRC computation table and routine
-
- even
- CRCtable dw 0, 4489, 8978, 12955, 17956, 22445, 25910, 29887
- dw 35912, 40385, 44890, 48851, 51820, 56293, 59774, 63735
- dw 4225, 264, 13203, 8730, 22181, 18220, 30135, 25662
- dw 40137, 36160, 49115, 44626, 56045, 52068, 63999, 59510
- dw 8450, 12427, 528, 5017, 26406, 30383, 17460, 21949
- dw 44362, 48323, 36440, 40913, 60270, 64231, 51324, 55797
- dw 12675, 8202, 4753, 792, 30631, 26158, 21685, 17724
- dw 48587, 44098, 40665, 36688, 64495, 60006, 55549, 51572
- dw 16900, 21389, 24854, 28831, 1056, 5545, 10034, 14011
- dw 52812, 57285, 60766, 64727, 34920, 39393, 43898, 47859
- dw 21125, 17164, 29079, 24606, 5281, 1320, 14259, 9786
- dw 57037, 53060, 64991, 60502, 39145, 35168, 48123, 43634
- dw 25350, 29327, 16404, 20893, 9506, 13483, 1584, 6073
- dw 61262, 65223, 52316, 56789, 43370, 47331, 35448, 39921
- dw 29575, 25102, 20629, 16668, 13731, 9258, 5809, 1848
- dw 65487, 60998, 56541, 52564, 47595, 43106, 39673, 35696
- dw 33800, 38273, 42778, 46739, 49708, 54181, 57662, 61623
- dw 2112, 6601, 11090, 15067, 20068, 24557, 28022, 31999
- dw 38025, 34048, 47003, 42514, 53933, 49956, 61887, 57398
- dw 6337, 2376, 15315, 10842, 24293, 20332, 32247, 27774
- dw 42250, 46211, 34328, 38801, 58158, 62119, 49212, 53685
- dw 10562, 14539, 2640, 7129, 28518, 32495, 19572, 24061
- dw 46475, 41986, 38553, 34576, 62383, 57894, 53437, 49460
- dw 14787, 10314, 6865, 2904, 32743, 28270, 23797, 19836
- dw 50700, 55173, 58654, 62615, 32808, 37281, 41786, 45747
- dw 19012, 23501, 26966, 30943, 3168, 7657, 12146, 16123
- dw 54925, 50948, 62879, 58390, 37033, 33056, 46011, 41522
- dw 23237, 19276, 31191, 26718, 7393, 3432, 16371, 11898
- dw 59150, 63111, 50204, 54677, 41258, 45219, 33336, 37809
- dw 27462, 31439, 18516, 23005, 11618, 15595, 3696, 8185
- dw 63375, 58886, 54429, 50452, 45483, 40994, 37561, 33584
- dw 31687, 27214, 22741, 18780, 15843, 11370, 7921, 3960
-
- CRCpass: ;input: dx=partial CRC
- push bx ; ah=char to process
- push ax
- xor dl,ah
- mov bl,dl
- xor bh,bh
- add bx,bx
- mov dl,dh
- xor dh,dh
- xor dx,[CRCtable+bx]
- pop ax
- pop bx
- ret
-
- ;CRC in DX must be initialized with 0FFFFh
- ;and inverted after passing through all characters
- ;==============================================================
- ;here is the installation and de-installation code
-
- deinstall_driver:
- mov al,packet_int_no ;disconnect DRVR_ISR
- mov ah,25h
- push ds
- lds dx,dword ptr old_packet_int
- int 21h
- pop ds
-
- call restore_COM ;restore COM port state
- ;and interrupt vector(s)
- call Restore_Timer ;restore Timer interrupt
-
- push cs ;free memory
- pop es
- mov ah,49h
- int 21h
- ret
-
- end_resident: ;all code after this point will not stay resident
- ;after the installation is done.
-
- Int_is_busy: ;there is already a packet driver installed
- ;at specified software interrupt
- call Print_following_string
- db 'There is already a packet driver at interrupt 0x',0
- mov dl,packet_int_no
- call Print_DL_hex
- DoNotInstall:
- call Print_following_string
- db 13,10,BOLD,'AX.25 driver has _not_ been installed',NORM,13,10,0
- mov ax,4C00h ;terminate but don't stay resident
- int 21h
- ret
-
- BadUsage:
- call Print_following_string
- db 13,10,0
- mov bx,offset Msg_usage
- call Print_BX_string
- jmp short DoNotInstall
-
- install_driver:
-
- call Print_following_string
- db NORM
- db 'AX.25 packet driver for RS232 port by Pawel Jalocha',13,10
- db 'Version of 4th January 1993',13,10
- db 'Free licence is granted for radio _amateurs_ only',13,10,0
-
- call ReadOptions
- jnc DoPrintParam
- jmp BadUsage
- DoPrintParam:
- call PrintParameters
- call CheckParameters
- jnc DoComputeSec
- jmp DoNotInstall
- DoComputeSec:
- call ComputeSecondaryPar
-
- push es
-
- mov al,packet_int_no ;save int vector
- mov ah,35h
- int 21h
- mov old_packet_int,bx
- mov ax,es
- mov old_packet_int+2,ax
-
- add bx,3 ;check if there is already a packet driver
- mov di,bx
- mov si,offset DRVR_ISR +3
- mov cx,9
- cmp_char: mov al,[si]
- inc si
- cmp al,es:[di]
- jne Int_is_free
- inc di
- loop cmp_char
- jmp Int_is_busy
- Int_is_free:
-
- mov al,packet_int_no ;put a new one in place
- mov ah,25h
- mov dx,offset DRVR_ISR
- int 21h ;ds must be equal to cs here
-
- mov es,phd_environ ;release our environment
- mov ah,49h
- int 21h
-
- pop es
-
- ;initialize COM port and interrupt vector(s)
- call initialize_COM
- ;initialize Timer routine
- call Initialize_Timer
-
- call Print_following_string
- db BOLD,'AX.25 driver is now installed and initialized',NORM,13,10,0
-
- ;make the code resident
- mov ax,3100h
- mov dx,offset end_resident + 0Fh
- mov cl,4
- shr dx,cl
- int 21h
- ret
-
- Msg_usage:
- db 'ax25 options: (default values in [])',13,10
- db BOLD,'-?',NORM,' prints this help message',13,10
- db BOLD,'-i',NORM,'<int_no>(hex) software interrupt number [60]',13,10
- db BOLD,'-I',NORM,'<irq>(hex)[p][s] COM IRQ number 2..7 [4]',13,10
- db BOLD,'-B',NORM,'<base>(hex) COM base address 0..3ff [3f8]',13,10
- db BOLD,'-b',NORM,'<bit rate>(dec) [1200]',13,10
- db BOLD,'-c',NORM,'<carrier mode> possible choices are:',13,10
- db ' -cf = full duplex (do not care about channel busy - just transmit)',13,10
- db ' -cc = sense DCD modem line',13,10
- db ' -ct = sense data transitions [default] (hardware DCD)',13,10
- db ' -cd = deliver carrier signal from data analysis (software DCD)',13,10
- db BOLD,'-T',NORM,'<threshold>(dec) software DCD threshold 0..100 [50]',13,10
- db BOLD,'-s',NORM,'<slot time>(dec) slot time in data bits [120]',13,10
- db BOLD,'-p',NORM,'<peristance>(dec) persistance/255 [64]',13,10
- db BOLD,'-h',NORM,'<tx head>(dec) Transmitter head in data bit units [240]',13,10
- db BOLD,'-t',NORM,'<tx tail>(dec) transmitter tail in data bit units [24]',13,10
- db BOLD,'-S',NORM,' sound effects (beeps for every received frame)',13,10
- db 0
-
- ;==============================================================
- ;Here are some routine for printing numbers and strings
-
- print_following_string: ;prints string following the call - modifies bx !
- ;string must following "call print_following_string"
- ;_must_ end with NULL character !
- pop bx ;pop return address from the stack
- push ax ;so we know where the char. string is
- push dx
- print_next_char:
- mov dl,cs:[bx] ;load next character
- inc bx
- and dl,dl ;NULL char ?
- jz string_end ;exit this loop if so
- mov ah,2 ;otherwise print it
- int 21h
- jmp short print_next_char
- string_end:
- pop dx
- pop ax
- push bx ;push new return address on stack
- ret
- print_BX_string: ;prints string addressed by ds:bx
- push ax ;the string must be terminated by NULL char
- push bx
- push dx
- print_next_BX_char: ;loop over characters
- mov dl,[bx] ;read next character
- inc bx
- and dl,dl ;NULL char ?
- jz BX_string_end ;jump if so
- mov ah,2 ;otherwise print it
- int 21h
- jmp short print_next_BX_char
- BX_string_end:
- pop dx
- pop bx
- pop ax
- ret
-
- print_DL_hex: ;prints in hex byte stored in DL
- push ax
- push cx
- mov cl,4
- jmp short print_low_byte
- print_DX_hex: ;prints in hex word stored in DX
- push ax
- push cx
- mov cl,4
- mov al,dh
- rol al,cl
- call print_hex_digit
- rol al,cl
- call print_hex_digit
- print_low_byte:
- mov al,dl
- rol al,cl
- call print_hex_digit
- rol al,cl
- call print_hex_digit
- pop cx
- pop ax
- ret
-
- print_hex_digit: ;prints hex digit stored in AL
- push ax
- push dx
- and al,0Fh
- cmp al,10
- jc add_0
- add al,'a'-'0'-10
- add_0: add al,'0'
- mov dl,al
- mov ah,2
- int 21h
- pop dx
- pop ax
- ret
-
- print_DX_dec: ;print in decimal word stored in DX
- push ax
- push bx
- push cx
- push dx
- mov bx,10
- xor cx,cx
- mov ax,dx
- calc_next_dig:
- xor dx,dx
- div bx
- push dx
- inc cx
- and ax,ax
- jnz calc_next_dig
- print_next_dig:
- pop ax
- call print_dec_digit
- loop print_next_dig
- pop dx
- pop cx
- pop bx
- pop ax
- ret
- print_dec_digit: ;prints in dec digit stored in AL
- push ax
- push dx
- add al,48
- mov dl,al
- mov ah,2
- int 21h
- pop dx
- pop ax
- ret
-
- ;==============================================================
- ;Routines to interprete input
-
- SkipBlanks: ;mov to first non-SPACE nor TAB char.
- ;ds:[bx] = string address
- SkipThisChar:
- mov al,[bx] ;load next char.
- inc bx ;increment pointer
- cmp al,' ' ;space ?
- jz SkipThisChar ;jump if SPACE
- cmp al,9 ;TAB ?
- jz SkipThisChar ;jump if TAB
- dec bx ;if not SPACE nor TAB move pointer back
- ret ;ds:bx=address of non-blank character
- ;al=this character
-
- ReadOptions:
- push ax
- push bx
- push cx
- push dx
- mov bx,81h ;load offset to command line arguments
- NextOption:
- call SkipBlanks ;al=next non-blank char
- cmp al,13 ;carriage return ?
- jz ReadOptions_ret
- cmp al,'-' ;minus sign ?
- jz InterpreteOption ;if so go and interprete following chars
- PrintOptionUsage: ;otherwise set carry to indicate a problem
- ; mov bx,offset Msg_usage
- ; call Print_BX_string
- ReadOptions_err:
- stc
- ReadOptions_ret:
- pop dx
- pop cx
- pop bx
- pop ax
- ret ;carry=1 => results are _not_ valid
-
- InterpreteOption: ;interprete an option
- ;ds:bx=address of '-' char.
- inc bx
- mov al,[bx] ;load char after '-'
- inc bx ;move pointer futher
- cmp al,'?' ;question mark ?
- jz PrintOptionUsage
- cmp al,'B' ;B ? (COM base address)
- jz Opt_base
- cmp al,'i'
- jz Opt_interrupt
- cmp al,'b'
- jz Opt_baud
- cmp al,'s'
- jz Opt_slot
- cmp al,'S'
- jz Opt_sound
- cmp al,'p'
- jz Opt_persistance
- cmp al,'h'
- jz Opt_head
- cmp al,'t'
- jz Opt_tail
- cmp al,'T'
- jz Opt_DCD_thres
- cmp al,'I'
- jz Opt_irq
- cmp al,'c'
- jz Opt_carrier
-
- call UnknownOption ;if non of the above says the option
- ;was not recognized
- jmp ReadOptions_err
-
- Opt_base: ;COM base option
- call ReadHexNumber ;read hex number following -B
- mov com_base,dx ;save it in com_base
- jmp short NextOption
- Opt_interrupt:
- call ReadHexNumber
- mov packet_int_no,dl
- jmp short NextOption
- Opt_baud:
- call ReadDecNumber
- mov bit_rate,dx
- jmp short NextOption
- Opt_slot:
- call ReadDecNumber
- mov slot_time,dl
- jmp short NextOption
- Opt_sound:
- mov dl,0FFh
- mov sound,dl
- jmp short NextOption
- Opt_persistance:
- call ReadDecNumber
- mov persistance,dl
- jmp short NextOption
- Opt_head:
- call ReadDecNumber
- mov tx_head,dx
- jmp NextOption
- Opt_tail:
- call ReadDecNumber
- mov tx_tail,dx
- jmp NextOption
- Opt_DCD_thres:
- call ReadDecNumber
- mov dcd_thres,dx
- jmp NextOption
-
-
- Opt_irq: ;COM irq option
- call ReadHexNumber ;read hex number following -I
- mov com_irq,dl ;save it in com_irq
- irq_sub_opt: ;irq sub-options
- cmp al,'p' ;is there 'p' after the irq number ?
- jz irq_prio
- cmp al,'s' ;is there 's' after the irq number ?
- jz irq_share
- jmp NextOption
-
- irq_prio:
- mov al,0FFh
- mov com_irq_prio,al
- inc bx
- mov al,[bx]
- jmp short irq_sub_opt
-
- irq_share:
- mov al,0FFh
- mov com_irq_share,al
- inc bx
- mov al,[bx]
- jmp short irq_sub_opt
-
- Opt_carrier: ;carrier detect method
- mov al,[bx]
- cmp al,'f'
- jz Carr_0
- cmp al,'c'
- jz Carr_1
- cmp al,'t'
- jz Carr_2
- cmp al,'d'
- jz Carr_3
- call UnknownCarrierOpt
- jmp ReadOptions_err
- Opt_carr_end:
- inc bx
- mov Carrier_sense,al
- jmp NextOption
-
- Carr_0: mov al,0
- jmp short Opt_carr_end
- Carr_1: mov al,1
- jmp short Opt_carr_end
- Carr_2: mov al,2
- jmp short Opt_carr_end
- Carr_3: mov al,3
- jmp short Opt_carr_end
-
- UnknownCarrierOpt: ;say the carrier option was not recognized
- push bx
- call Print_following_string
- db BOLD,BLINK,'Unknown option: -c',0
- mov dl,al
- mov ah,2
- int 21h
- call Print_following_string
- db NORM,13,10,0
- pop bx
- ret
-
- UnknownOption: ;say the option not recognized
- push bx
- call Print_following_string
- db BOLD,BLINK,'Unknown option: -',0
- mov dl,al
- mov ah,2
- int 21h
- call Print_following_string
- db NORM,13,10,0
- pop bx
- ret
-
- Param_OK db 0 ;"parameters are OK" flag
-
- CheckParameters:
- push bx
- push ax
- mov al,1
- mov Param_OK,al
-
- call Print_following_string
- db BOLD,BLINK,0
-
- ; mov al,packet_int_no
- ; cmp al,60h
- ; jnc int_no_OK
- ; call Print_following_string
- ; db 'Packet interrupt number is below 0x60',13,10,0
- ; xor al,al
- ; mov Param_OK,al
- ;int_no_OK:
-
- mov ax,bit_rate ;check bit rate
- cmp ax,300 ;must be >= 300
- jnc baud_upp
- call Print_following_string
- db 'Bauds below 300 bps not supported',13,10,0
- xor al,al
- mov Param_OK,al
- baud_upp:
- cmp ax,14400+1 ;but as well <= 14400
- jc baud_OK
- call Print_following_string
- db 'Bauds above 14400 bps not supported',13,10,0
- xor al,al
- mov Param_OK,al
- baud_OK:
- mov ax,tx_head ;tx head (TxDelay)
- cmp ax,8 ;must be >= 8 bits
- jnc tx_head_OK
- call Print_following_string
- db 'Tx head should be at least 8 bits',13,10,0
- xor al,al
- mov Param_OK,al
- tx_head_OK:
- mov ax,tx_tail ;tx tail
- cmp ax,8 ;must be >= 8 bits
- jnc tx_tail_OK
- call Print_following_string
- db 'Tx tail should be at least 8 bits',13,10,0
- xor al,al
- mov Param_OK,al
- tx_tail_OK:
- mov al,slot_time ;slot time
- cmp al,8 ;must be >= 8 bits
- jnc slot_time_OK
- call Print_following_string
- db 'Slot time should be at least 8 bits',13,10,0
- xor al,al
- mov Param_OK,al
- slot_time_OK:
- mov ax,com_base ;com base
- cmp ax,400h ;must be < 400h
- jc com_base_OK
- call Print_following_string
- db 'COM base address should be in the range 0..3ff',13,10,0
- xor al,al
- mov Param_OK,al
- com_base_OK:
- mov al,com_irq ;com irq
- cmp al,2 ;must be > 2
- jnc com_irq_upp
- wrong_irq:
- call Print_following_string
- db 'COM irq should be between 2 and 7',13,10,0
- xor al,al
- mov Param_OK,al
- jmp short com_irq_OK
- com_irq_upp:
- cmp al,8 ;and < 8 as well
- jnc wrong_irq
- com_irq_OK:
- mov ax,dcd_thres
- cmp ax,101
- jc dcd_thres_OK
- call Print_following_string
- db 'DCD threshold must be between 0 and 100',13,10,0
- xor al,al
- mov Param_OK,al
- dcd_thres_OK:
-
- mov al,Param_OK
- shr al,1
- cmc
- jnc CheckParam_ret
- ; call Print_following_string
- ; db 'ax25 driver can not accept these parameters',7,13,10,0
- stc
-
- CheckParam_ret:
- pushf ;save flags to preserve carry
- call Print_following_string
- db NORM,0
- popf
-
- pop bx
- pop ax
- ret
-
- ComputeSecondaryPar: ;computes secondary parameters
- ;after the primary ones are defined
- push ax
- push bx
- push cx
- push dx
-
- mov al,1 ;take com_irq
- mov cl,com_irq
- shl al,cl ;and compute bit mask
- mov irq_mask,al
-
- ;compute bit length for the transmitter
- ;in UART baud rate generator ticks
- mov ax,14400
- xor dx,dx ;dx:ax := 14400
- mov cx,bit_rate ;cx := bit_rate
- mov bx,cx
- shr bx,1 ;bx := bit_rate/2
- add ax,bx ;dx:ax := 14400 + bit_rate/2
- adc dx,0
- div cx ;ax:=(14400 + bit_rate/2) div bit_rate
- mov bd_bit_len,al ;save bit length
-
- mov ax,14400 ;correct bit rate to a round value
- xor dx,dx ;dx:ax := 14400
- mov cl,bd_bit_len
- xor ch,ch ;cx := bd_bit_len
- div cx ;ax := 14400 div bd_bit_len
- xchg ax,bit_rate ;bit_rate <-> ax
- cmp ax,bit_rate ;rates are indeed different ?
- jz Compute_cl_bit_len ;jump if not
- call Print_following_string ;notify about speed adjustments
- db 'Bit rate adjusted to ',0
- mov dx,bit_rate
- call Print_DX_dec
- call Print_following_string
- db ' bps',13,10,0
-
- Compute_cl_bit_len: ;compute bit length for the receiver
- ;in timer ticks
- mov ax,13532
- mov dx,18 ;dx:ax := 1193180
- mov cx,bit_rate ;cx := bit_rate
- mov bx,cx
- shr bx,1 ;bx := bit_rate/2
- add ax,bx ;dx:ax := 1193180 + bit_rate/2
- adc dx,0
- div cx ;ax := (1193180 + bit_rate/2) div bit_rate
- mov cl_bit_len,ax ;save bit length
- shr ax,1 ;ax := bit_length /2
- adc ax,0 ;if carry then ax+=1
- mov cl_bit_len_2,ax ;save half bit length
-
- mov ax,100
- sub ax,dcd_thres ;ax := 100 - dcd_thres
- mul cl_bit_len ;ax := ((100 - dcd_thres)*cl_bit_len)
- mov cx,400
- div cx ;ax := ax div 400
- mov cl_dcd_thres,ax ;save threshold for software DCD
-
- mov ax,cl_bit_len ;compute walk_step for DPLL
- xor dx,dx
- mov cl,walk_step_div
- xor ch,ch
- div cx
- mov walk_step,ax
-
- mov ah,bd_bit_len
- mov al,slot_time
- mul ah ;ax := bd_bit_len * slot_time
- mov bd_slot_time,ax
-
- pop dx
- pop cx
- pop bx
- pop ax
- ret
-
- PrintParameters: ;prints actual parameters of the driver
- push ax
- push bx
- push cx
- push dx
- call print_following_string
- db 'Actual ax25 driver parameters:',13,10,0
-
- call Print_following_string
- db 'Service interrupt ',BOLD,'0x',0
- mov dl,packet_int_no
- call Print_DL_hex
- call Print_following_string
- db NORM,13,10,'COM I/O base ',BOLD,'0x',0
- mov dx,com_base
- call Print_DX_hex
-
- call Print_following_string
- db NORM,' IRQ ',BOLD,'0x',0
- mov dl,com_irq
- call Print_DL_hex
-
- mov al,com_irq_prio
- and al,al
- jz skip_irq_prio
- call Print_following_string
- db ' [prior]',0
- skip_irq_prio:
-
- mov al,com_irq_share
- and al,al
- jz skip_irq_shared
- call Print_following_string
- db ' [shared]',0
- skip_irq_shared:
-
- call Print_following_string
- db NORM,' Bit rate ',BOLD,0
- mov dx,bit_rate
- mov cx,dx ;keep data rate in cx
- call Print_DX_dec
- call Print_following_string
- db ' bps',NORM,13,10,'Tx head ',BOLD,0
- mov dx,tx_head
- call Print_DX_dec
- call Print_following_string
- db ' bits ',NORM,'(',0
- mov ax,1000 ;compute tx_head in ms units
- mul dx
- div cx ;ax=tx_head*1000/bit_rate
- mov dx,ax
- call print_DX_dec
- call Print_following_string
- db 'ms) Tx tail ',BOLD,0
- mov dx,tx_tail
- call Print_DX_dec
- call Print_following_string
- db ' bits ',NORM,'(',0
- mov ax,1000 ;compute tx_tail in ms units
- mul dx
- div cx ;ax=tx_tail*1000/bit_rate
- mov dx,ax
- call Print_DX_dec
- call Print_following_string
- db 'ms)',13,10,'Slot time ',BOLD,0
- mov dl,slot_time
- xor dh,dh
- call Print_DX_dec
- call Print_following_string
- db ' bits ',NORM,'(',0
- mov ax,1000 ;compute slot_time in ms units
- mul dx
- div cx ;ax=slot_time*1000/bit_rate
- mov dx,ax
- call Print_DX_dec
- call Print_following_string
- db 'ms) p-persistance ',BOLD,0
- mov dl,persistance
- xor dh,dh
- call Print_DX_dec
- call Print_following_string
- db NORM,'/255',13,10,'Carrier mode: ',BOLD,0
- mov bl,carrier_sense
- xor bh,bh
- add bx,bx
- mov bx,[msg_carrier_modes+bx]
- call Print_BX_string
- call Print_following_string
- db 13,10,NORM,0
-
- mov al,carrier_sense
- cmp al,3
- jnz PrintSoundParam
- call Print_following_string
- db 'Software DCD threshold: ',BOLD,0
- mov dx,dcd_thres
- call Print_DX_dec
- call Print_following_string
- db NORM,13,10,0
- PrintSoundParam:
- mov al,sound
- and al,al
- jz PrintParameters_ret
- call Print_following_string
- db BOLD,'Sound effects',NORM,' activated',13,10,0
- PrintParameters_ret:
- call Print_following_string
- db 13,10,0
-
- pop dx
- pop cx
- pop bx
- pop ax
- ret
-
- even
- msg_carrier_modes label word
- dw offset Msg_full_duplex
- dw offset Msg_sense_DCD
- dw offset Msg_sense_trans
- dw offset Msg_sense_DPLL
-
- Msg_full_duplex db 'do not sense carrier => full duplex',0
- Msg_sense_DCD db 'sense DCD modem line',0
- Msg_sense_trans db 'sense data transitions',0
- Msg_sense_DPLL db 'deliver carrier from data analysis',0
-
- ReadDecDigit: ;ds:bx points to a character
- mov al,[bx]
- cmp al,'0'
- jc ReadDecDigit_ret ;jump if below '0'
- cmp al,'9'+1
- cmc
- jc ReadDecDigit_Ret ;jump if above '9'
- sub al,'0'
- ReadDecDigit_ret:
- ret ;carry=1 if not a dec digit => then al=the character
- ;otherwise al=digit value
-
- ReadDecNumber: ;ds:bx points to the first digit
- push cx
- mov dx,0 ;dx will contain the number
- ReadNextDecDigit:
- call ReadDecDigit ;read next digit - al=digit value
- jc ReadDecNumber_ret ;jump if this was not a digit
- inc bx ;increment pointer
- xor ah,ah ;ax=digit value
- add dx,dx ;multiply dx by 2
- mov cx,dx ;multiply dx by 5
- add dx,dx
- add dx,dx
- add dx,cx
- add dx,ax ;add the digit value just read
- jmp short ReadNextDecDigit
- ReadDecNumber_ret:
- pop cx
- ret ;dx=the number,
- ;ds:bx *char where the interpretation stopped
- ;al=this character, ah possibly modified
-
- ;the routine below accepts only _lowercase_ letters as hex numbers
- ReadHexDigit: ;ds:bx = digit pointer
- mov al,[bx]
- cmp al,'0'
- jc ReadHexDigit_ret ;jump if below '0'
- cmp al,'9'+1
- cmc
- jc ReadHexDigit_lett ;jump if above '9'
- sub al,'0'
- jmp short ReadHexDigit_ret
- ReadHexDigit_lett:
- cmp al,'a'
- jc ReadHexDigit_ret ;jump if below 'a'
- cmp al,'f'+1
- cmc
- jc ReadHexDigit_ret ;jump if above 'f'
- sub al,'a'-10
- ReadHexDigit_ret:
- ret ;carry=1 => not a hex digit, al=char.
- ;carry=0 => hex digit indeed, al=value
-
- ReadHexNumber:
- push cx
- mov cl,4
- mov dx,0
- ReadNextHexDigit:
- call ReadHexDigit
- jc ReadHexNumber_ret
- inc bx
- shl dx,cl ;multiply dx by 16
- or dl,al ;add the digit just read
- jmp short ReadNextHexDigit
- ReadHexNumber_ret:
- pop cx
- ret ;dx=the number,
- ;ds:bx *char where the interpretation stopped
- ;al=this character
-
- ;==============================================================
-
- ax25_code ends
-
- end start
-